package v3

import (
	"crypto"
	crand "crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/gogap/logs"
	"hash"
	"math/rand"
	"strings"
	"time"
)

func (c *ClientV3) GenAuthString() string {
	if c.AppCertSN != "" {
		return fmt.Sprintf("app_id=%s,app_cert_sn=%s,nonce=%s,timestamp=%s", c.AppId, c.AppCertSN, c.RandomStr(32), time.Now().Unix())
	} else {
		return fmt.Sprintf("app_id=%s,nonce=%s,timestamp=%d", c.AppId, c.RandomStr(32), time.Now().Unix())
	}
}

func (c *ClientV3) GenContent(authString, httpMethod, requestUrl, requestBody, authToken string) string {
	if authToken != "" {
		return fmt.Sprintf("%s\n%s\n%s\n%s\n%s", authString, httpMethod, requestUrl, requestBody, authToken)
	} else {
		return fmt.Sprintf("%s\n%s\n%s\n%s\n", authString, httpMethod, requestUrl, requestBody)
	}
}

func (c *ClientV3) MakeSign(content, signType string) (sign string, err error) {
	var (
		h              hash.Hash
		hashs          crypto.Hash
		encryptedBytes []byte
	)

	switch signType {
	case RSA:
		h = sha1.New()
		hashs = crypto.SHA1
	case RSA2:
		h = sha256.New()
		hashs = crypto.SHA256
	default:
		h = sha256.New()
		hashs = crypto.SHA256
	}
	if _, err = h.Write([]byte(content)); err != nil {
		return
	}
	if encryptedBytes, err = rsa.SignPKCS1v15(crand.Reader, c.PrivateKey, hashs, h.Sum(nil)); err != nil {
		return
	}

	sign = base64.StdEncoding.EncodeToString(encryptedBytes)
	logs.Info("打印测试===========>content", content)
	return
}

func (c *ClientV3) VerifySign(headers map[string]string, body string) error {
	content := fmt.Sprintf("%s\n%s\n%s\n", headers["alipay-timestamp"], headers["alipay-nonce"], body)

	pKey := FormatPublicKey(c.AliPayPublicKey)

	if err := verifySign(content, headers["alipay-signature"], "RSA2", pKey); err != nil {
		return err
	}
	return nil
}

func (c *ClientV3) GentAuthorization(signType, authString, signature string) string {
	authorization := fmt.Sprintf(`%s %s,sign="%s"`, signType, authString, signature)
	return authorization
}

func (c *ClientV3) RandomStr(l int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := make([]byte, 0)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < l; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

// FormatPublicKey 格式化 普通支付宝公钥
func FormatPublicKey(publicKey string) (pKey string) {
	var buffer strings.Builder
	buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
	rawLen := 64
	keyLen := len(publicKey)
	raws := keyLen / rawLen
	temp := keyLen % rawLen
	if temp > 0 {
		raws++
	}
	start := 0
	end := start + rawLen
	for i := 0; i < raws; i++ {
		if i == raws-1 {
			buffer.WriteString(publicKey[start:])
		} else {
			buffer.WriteString(publicKey[start:end])
		}
		buffer.WriteByte('\n')
		start += rawLen
		end = start + rawLen
	}
	buffer.WriteString("-----END PUBLIC KEY-----\n")
	pKey = buffer.String()
	return
}

func verifySign(content, sign, signType, aliPayPublicKey string) (err error) {
	var (
		h         hash.Hash
		hashs     crypto.Hash
		block     *pem.Block
		pubKey    interface{}
		publicKey *rsa.PublicKey
		ok        bool
	)
	signBytes, _ := base64.StdEncoding.DecodeString(sign)
	if block, _ = pem.Decode([]byte(aliPayPublicKey)); block == nil {
		return errors.New("支付宝公钥Decode错误")
	}
	if pubKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
		return fmt.Errorf("x509.ParsePKIXPublicKey：%w", err)
	}
	if publicKey, ok = pubKey.(*rsa.PublicKey); !ok {
		return errors.New("支付宝公钥转换错误")
	}
	switch signType {
	case RSA:
		hashs = crypto.SHA1
	case RSA2:
		hashs = crypto.SHA256
	default:
		hashs = crypto.SHA256
	}
	h = hashs.New()
	h.Write([]byte(content))
	return rsa.VerifyPKCS1v15(publicKey, hashs, h.Sum(nil), signBytes)
}
